package mtt.model.record;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;

import mtt.model.calendar.Day;
import mtt.model.calendar.Event;
import mtt.model.calendar.Week;

public class MttCalendarRecordStore extends MttRecordStore {

	/**
	 * enthält die einzige instanz des RecordStores
	 */
	private static final MttCalendarRecordStore mttCalendarRecordStore = new MttCalendarRecordStore();

	/**
	 * enthält den Namen des RecordStores
	 * 
	 * @uml.property name="name" readOnly="true"
	 */
	public static final String name = "MttCalendarRecordStore";

	/**
	 * liefert die einzige Instanz des CalendarRecordStore
	 * 
	 * @throws RecordStoreException
	 * @throws RecordStoreNotFoundException
	 * @throws RecordStoreFullException
	 */
	public static final MttCalendarRecordStore getInstance()
			throws RecordStoreFullException, RecordStoreNotFoundException,
			RecordStoreException {

		/*
		 * ist der RecordStore schon geöffnet
		 */
		if (mttCalendarRecordStore.isOpen() == false) {
			openRecordStore(mttCalendarRecordStore);
		}

		return mttCalendarRecordStore;

	}

	/**
	 * privater Konstruktor, use Factory-Methode getInstance()
	 */
	private MttCalendarRecordStore() {

	}

	/**
	 * fügt einen neuen Event hinzu und setzt die Record-Id
	 * 
	 * @param Event
	 * @throws RecordStoreException
	 * @throws RecordStoreFullException
	 * @throws RecordStoreNotOpenException
	 * @throws IOException 
	 */
	public void addEvent(Event event) throws RecordStoreNotOpenException,
			RecordStoreFullException, RecordStoreException, IOException {
		// TODO termin auf gültigkeit prüfen

		/*
		 * Byte-Array erzeugen
		 */
		byte[] record = parseEvent(event);

		/*
		 * Record speichern
		 */
		int intId = addRecord(record);

		/*
		 * Record-Id zum Event hinzufügen
		 */
		event.setIntId(new Integer(intId));

		/*
		 * Event mit der seiner Id aktualisieren
		 */
		updateEvent(event);
	}

	/**
	 * entfernt ein Event aus dem RecordStore
	 * @throws RecordStoreException 
	 * @throws InvalidRecordIDException 
	 * @throws RecordStoreNotOpenException 
	 */
	public void deleteEvent(Event event) throws RecordStoreNotOpenException, InvalidRecordIDException, RecordStoreException {
		deleteRecord(event.getIntId().intValue());
	}

	public String getName() {
		return name;
	}

	/**
	 * lädt alle Events der übergebenen Woche, weekType muss gesetzt sein
	 * 
	 * @param week
	 * @throws RecordStoreNotOpenException
	 * @throws NullPointerException
	 *             if weekType is not set
	 */
	public void loadWeekEvents(Week week) throws RecordStoreNotOpenException,
			NullPointerException {

		if (isOpen() == false) {
			throw new NullPointerException("Der RecordStore " + getName()
					+ " ist noch nicht geöffnet.");
		}

		RecordEnumeration enumerator = getRecordStore().enumerateRecords(null,
				null, true);

		/*
		 * für welchen weekType sollen Termine abgefragt werden
		 */
		Integer weekIntervall = new Integer(week.getWeekType());

		while (enumerator.hasNextElement()) {
			try {
				/*
				 * Record holen
				 */
				byte[] record = enumerator.nextRecord();

				/*
				 * Record in Event parsen
				 */
				Event event = parseRecord(record);

				/*
				 * Event zur Liste hinzufügen
				 */
				if (event != null) {
					/*
					 * gehört der Event zur Woche
					 */
					if (event.getIntIntervall().intValue() == weekIntervall
							.intValue()) {
						Day day = week.getDay(event.getDayOfEvent());
						day.addEvent(event);
					}
				}
			} catch (InvalidRecordIDException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (RecordStoreException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	
	/**
	 * erstellt aus einem Event ein record-Datenstrom
	 * 
	 * Format: intId, intIntervall, intTyp, stringName, StringOrt, String.Dozent
	 * 
	 * @param der
	 *            Event der in ein byte-Array gewandelt werden soll
	 * @return byte-Array des Event-Objekt bzw null bei einem Fehler
	 * @throws IOException 
	 */
	private byte[] parseEvent(Event event) throws IOException {

		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

		DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

		// TODO prüfen ob Id schon vorhanden ist
		/*
		 * Integer Werte schreiben
		 */
			dataOutputStream.writeInt(event.getIntId() == null ? 0 : event
					.getIntId().intValue());
			dataOutputStream.writeInt(event.getIntIntervall().intValue());
			dataOutputStream.writeInt(event.getIntTyp().intValue());

			/*
			 * wirteUTF darf nicht null sein
			 */
			dataOutputStream.writeUTF(event.getStringName() == null ? "" : event.getStringName());
			dataOutputStream.writeUTF(event.getStringOrt() == null ? "" : event.getStringOrt());
			dataOutputStream.writeUTF(event.getStringTutor() == null ? "" : event.getStringTutor());

			/*
			 * dataOutputStream.close() schließt auch byteArrayOutputStream()
			 */
			dataOutputStream.close();
		
		/*
		 * Output-Stream als Byte-Array
		 */
		return byteArrayOutputStream.toByteArray();
	}
	
	/**
	 * erstellt aus einem record einen Event-Objekt
	 * 
	 * @param der
	 *            record aus dem der Event erstellt werden soll
	 * @return der Event aus dem record oder null bei einem Fehler
	 */
	private Event parseRecord(byte[] record) {

		// TODO prüfen ob immer ein neues Objekt angelegt werden muss
		// if (this.byteArrayInputStream == null) {
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
				record);
		// }

		// if (this.dataInputStream == null) {
		DataInputStream dataInputStream = new DataInputStream(
				byteArrayInputStream);
		// }

		try {
			/*
			 * Integer-Werte lesen
			 */
			int terminId = dataInputStream.readInt();
			int intervall = dataInputStream.readInt();
			int typ = dataInputStream.readInt();

			/*
			 * String-Werte lesen
			 */
			String name = dataInputStream.readUTF();
			String ort = dataInputStream.readUTF();
			String dozent = dataInputStream.readUTF();

			/*
			 * Event-Objekt erstellen und Daten eintragen
			 */
			Event event = new Event();

			event.setIntId(new Integer(terminId));
			event.setStringName(name);
			event.setIntTyp(new Integer(typ));
			event.setStringOrt(ort);
			event.setStringTutor(dozent);
			event.setIntIntervall(new Integer(intervall));

			return event;

		} catch (IOException e) {
			return null;
		}
	}
	
	/**
	 * aktualisiert einen Event, Event.intID muss gesetzt sein
	 * 
	 * @param der
	 *            zu aktualisierende Event
	 * @throws RecordStoreException
	 * @throws RecordStoreFullException
	 * @throws InvalidRecordIDException
	 * @throws RecordStoreNotOpenException
	 * @throws IOException 
	 */
	public void updateEvent(Event event) throws RecordStoreNotOpenException,
			InvalidRecordIDException, RecordStoreFullException,
			RecordStoreException, IOException {
		// TODO Event.intID prüfen

		byte[] record = parseEvent(event);
		updateRecord(record, event.getIntId().intValue());
	}
}
